package io.gitee.mingbaobaba.apijson.querycondition.query.conditions;


import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 * <p>构建参数基类</p>
 *
 * @author yingsheng.ye
 * @version 1.0.0
 * @since 2023/6/19 18:06
 */
@SuppressWarnings("unchecked")
public abstract class AbstractBaseWrapper<T, R, C extends AbstractBaseWrapper<T, R, C>> {


    protected final C typedThis = (C) this;

    /**
     * 数仓apiJson查询参数
     */
    protected Map<String, Object> apiJsonParams = new HashMap<>();

    /**
     * 条件列表存储
     */
    protected List<Condition> conditionList = new ArrayList<>();

    /**
     * 最后追加到参数列表
     */
    protected Map<String, Object> lastAppendMap = new HashMap<>();

    /**
     * 模式名
     */
    protected String schema;

    /**
     * 表名
     */
    protected String tableName;

    /**
     * 业务标识
     */
    protected String biSigns;

    /**
     * 查询列
     */
    protected Set<String> columnList = new HashSet<>();

    /**
     * 排序列
     */
    protected List<String> orderByList = new ArrayList<>();

    /**
     * 分组列
     */
    protected Set<String> groupList = new HashSet<>();

    /**
     * 分组条件
     */
    protected String havingFunc = null;

    /**
     * 实体类型
     */
    protected Class<T> entityClass;

    /**
     * 执行计划
     */
    protected Boolean explain = false;

    /**
     * 设置执行计划
     *
     * @param explain 是否开启执行计划
     * @return boolean true为开启 false不开启
     */
    public C setExplain(Boolean explain) {
        this.explain = explain;
        return typedThis;
    }

    /**
     * 设置 schema
     *
     * @param schema 模式
     * @return Children
     */
    public C setSchema(String schema) {
        this.schema = schema;
        return typedThis;
    }

    /**
     * 设置表名
     *
     * @param tableName 表名
     * @return Children
     */
    public C setTableName(String tableName) {
        this.tableName = tableName;
        return typedThis;
    }

    /**
     * 设置表名
     *
     * @param clazz 表名
     * @return Children
     */
    public C setTableName(Class<T> clazz) {
        this.entityClass = clazz;
        return typedThis;
    }

    /**
     * 业务标识
     *
     * @param val 业务标识值
     * @return Children
     */
    public C setBiSigns(String val) {
        this.biSigns = val;
        return typedThis;
    }


    /**
     * 构建查询条件
     *
     * @param clazz 类
     * @return C
     */
    public C select(Class<T> clazz) {
        Field[] fields = getFields(clazz);
        for (Field f : fields) {
            ApiJsonTableField dwTableField = f.getAnnotation(ApiJsonTableField.class);
            String name;
            if (null == dwTableField) {
                name = f.getName();
            } else {
                if (!dwTableField.exist() || StringUtils.isBlank(dwTableField.value())) {
                    continue;
                }
                name = dwTableField.value();
            }
            columnList.add(name);
        }
        return typedThis;
    }

    /**
     * 获取类以及父类所有属性
     *
     * @param clazz Class
     * @return Field[]
     */
    private Field[] getFields(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        fieldList.toArray(fields);
        return fields;
    }


    /**
     * 构建结果
     */
    public void builder() {
        //构建查询列
        buildColumn();
        //构建查询参数
        buildParams();
        //构建分组
        buildGroup();
        //构建排序
        buildOrder();
        //构建执行计划
        buildExplain();
        //拼接参数
        buildAppend();
    }

    /**
     * 构建拼接参数
     */
    private void buildAppend() {
        if (!CollectionUtils.isEmpty(lastAppendMap)) {
            apiJsonParams.putAll(lastAppendMap);
        }
    }

    /**
     * 构建执行计划
     */
    private void buildExplain() {
        if (Boolean.TRUE.equals(explain)) {
            apiJsonParams.put(EnumApiJson.EXPLAIN.getCode(), true);
        }
    }

    /**
     * 构建查询列
     */
    protected void buildColumn() {
        //查询列构建
        if (CollectionUtils.isEmpty(columnList) && null != entityClass) {
            this.select(entityClass);
        }
        apiJsonParams.put(EnumApiJson.COLUMN.getCode(), join(";", columnList));
    }

    /**
     * 构建条件参数
     */
    protected void buildParams() {
        if (conditionList.isEmpty()) {
            return;
        }
        //判断是否包含条件
        if (conditionList.stream().anyMatch(condition -> condition.getKeyword().equals(EnumKeyword.OR)
                || condition.getKeyword().equals(EnumKeyword.AND) || condition.getKeyword().equals(EnumKeyword.NOT))
        ) {
            setKeysBuilder();
        }
        setConditionParam();
    }

    /**
     * 设置连接参数
     */
    private void setKeysBuilder() {
        StringBuilder keysBuilder = new StringBuilder();
        conditionList.forEach(condition -> {
            if (condition.getKeyword().equals(EnumKeyword.OR)) {
                keysBuilder.append("|");
            } else if (condition.getKeyword().equals(EnumKeyword.AND)) {
                keysBuilder.append("&");
            } else if (condition.getKeyword().equals(EnumKeyword.NOT)) {
                //非
                keysBuilder.append("!");
            } else {
                String column = getConditionColumn(condition);
                if (StringUtils.isNotBlank(column)) {
                    keysBuilder.append(column);
                    keysBuilder.append(",");
                }
            }
        });
        String keys = keysBuilder.toString();
        //条件不为空或者条件数大于1
        if (StringUtils.isNotBlank(keys) && keys.split(",").length >= 2) {
            apiJsonParams.put(EnumApiJson.COMBINE.getCode(), keys.endsWith(",") ? keys.substring(0, keys.length() - 1) : keys);
        }
    }

    /**
     * 设置查询条件
     */
    private void setConditionParam() {
        conditionList.forEach(condition -> {
            String column = getConditionColumn(condition);
            if (StringUtils.isNotBlank(column)) {
                apiJsonParams.put(column, condition.getVal());
            }
        });
    }

    /**
     * 构建分组
     */
    protected void buildGroup() {
        if (!CollectionUtils.isEmpty(groupList)) {
            apiJsonParams.put(EnumApiJson.GROUP.getCode(), join(",", groupList));
            //构建having条件
            if (StringUtils.isNotBlank(havingFunc)) {
                apiJsonParams.put(EnumApiJson.HAVING.getCode(), havingFunc);
            }
        }
    }

    /**
     * 构建排序参数
     */
    protected void buildOrder() {
        if (!CollectionUtils.isEmpty(orderByList)) {
            //构建排序
            StringBuilder orderByBuilder = new StringBuilder();
            for (int i = 0; i < orderByList.size(); i++) {
                String columnBy = orderByList.get(i);
                orderByBuilder.append(columnBy);
                if (i < orderByList.size() - 1) {
                    orderByBuilder.append(",");
                }
            }
            apiJsonParams.put(EnumApiJson.ORDER.getCode(), orderByBuilder.toString());
        }
    }


    /**
     * 获取条件列
     *
     * @param condition Condition
     * @return 条件列
     */
    private String getConditionColumn(Condition condition) {

        String column;
        switch (condition.getKeyword()) {
            case NULL:
            case NOT_NULL:
            case IN:
                column = condition.getColumn() + "{}";
                break;
            case NOT_IN:
                column = condition.getColumn() + "!{}";
                break;
            case LIKE:
                column = condition.getColumn() + "$";
                break;
            case NOT_LIKE:
                column = condition.getColumn() + "!$";
                break;
            case EQ:
                column = condition.getColumn();
                break;
            case NE:
                column = condition.getColumn() + "!";
                break;
            case GT:
                column = condition.getColumn() + ">";
                break;
            case GE:
                column = condition.getColumn() + ">=";
                break;
            case LT:
                column = condition.getColumn() + "<";
                break;
            case LE:
                column = condition.getColumn() + "<=";
                break;
            case REG_EXP:
                column = condition.getColumn() + "~";
                break;
            case BETWEEN_AND:
                column = condition.getColumn() + "%";
                break;
            default:
                column = "";
                break;
        }
        return column;
    }


    /**
     * 获取apiJson参数
     *
     * @return Map<String, Object>
     */
    public Map<String, Object> getApiJsonParam() {
        if (apiJsonParams.isEmpty()) {
            this.builder();
        }
        return apiJsonParams;
    }

    /**
     * 获取模式
     *
     * @return String
     */
    public String getSchema() {
        if (StringUtils.isBlank(schema) && null != entityClass) {
            //判断是否有table注解
            ApiJsonTableName dwTable = entityClass.getAnnotation(ApiJsonTableName.class);
            if (null != dwTable && StringUtils.isNotBlank(dwTable.schema())) {
                //设置Schema
                schema = dwTable.schema();
            }
        }
        Assert.isTrue(StringUtils.isNotBlank(schema), "查询schema不能为空");
        return schema;
    }

    /**
     * 获取表
     *
     * @return String
     */
    public String getTable() {
        if (StringUtils.isBlank(tableName) && null != entityClass) {
            tableName = entityClass.getSimpleName();
            //判断是否有table注解
            ApiJsonTableName dwTable = entityClass.getAnnotation(ApiJsonTableName.class);
            if (null != dwTable && StringUtils.isNotBlank(dwTable.value())) {
                //设置表名
                tableName = dwTable.value();
            }
        }
        Assert.isTrue(StringUtils.isNotBlank(tableName), "查询表名不能为空");
        return tableName;
    }

    /**
     * 获取业务标示
     *
     * @return String
     */
    public String getBiSigns() {
        if (StringUtils.isBlank(biSigns) && null != entityClass) {
            //判断是否有table注解
            ApiJsonTableName dwTable = entityClass.getAnnotation(ApiJsonTableName.class);
            if (null != dwTable && StringUtils.isNotBlank(dwTable.biSigns())) {
                //设置业务标识
                biSigns = dwTable.biSigns();
            }
        }
        Assert.isTrue(StringUtils.isNotBlank(biSigns), "查询业务标识不能为空");
        return biSigns;
    }

    /**
     * 清空条件
     */
    public void clear() {
        this.schema = null;
        this.tableName = null;
        this.biSigns = null;
        this.entityClass = null;
        this.explain = false;
        this.conditionList.clear();
        this.columnList.clear();
        this.orderByList.clear();
        this.havingFunc = null;
        this.groupList.clear();
        this.lastAppendMap.clear();
        this.apiJsonParams.clear();
    }

    /**
     * 将集合转为字符串
     *
     * @param delimiter  分隔符
     * @param collection 集合
     * @return String
     */
    private String join(String delimiter, Collection<String> collection) {
        StringBuilder stringBuilder = new StringBuilder();
        for (String item : collection) {
            stringBuilder.append(item).append(delimiter);
        }
        return stringBuilder.toString();
    }

}
